/*
 *  Copyright (C) 2010 Glauco
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.transaction;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.Serializable;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import org.balisunrise.annotation.EventType;
import org.balisunrise.builder.BuilderException;
import org.balisunrise.application.Application;
import org.balisunrise.builder.CommonFactory;
import org.balisunrise.application.BaliProperties;
import org.balisunrise.builder.FactoryException;
import org.balisunrise.application.View;
import org.balisunrise.common.Criterion;
import org.balisunrise.common.BaliIcons;
import org.balisunrise.common.EntityFieldType;
import org.balisunrise.common.Query;
import org.balisunrise.common.EntityLoader;
import org.balisunrise.common.Event;
import org.balisunrise.common.FieldComponent;
import org.balisunrise.common.FieldInput;
import org.balisunrise.common.VisualEntityType;
import org.balisunrise.common.Search;
import org.balisunrise.common.Messenger;
import org.balisunrise.common.SelectionFieldLayout;
import org.balisunrise.common.abstracts.AbstractVisualizable;
import org.balisunrise.common.defaults.VEAUtil;
import org.balisunrise.swing.components.util.FocusTraversal;

/**
 *
 * @author Glauco
 */
public class SelectionAction extends AbstractVisualizable
        implements org.balisunrise.common.SelectionAction<JLabel>,
        ActionListener {

    private Class searchClass;
    private String searchAlias;
    private String notFoundMessage;
    private SelectionFieldLayout selectionFieldLayout;
    private JLabel visualRepresentation;
    private Search search;
    private EntityLoader entityLoader;
    private EntitySelection entitySelection;
    private View searchView;
    private View selectionView;
    private FocusTraversal focusTraversal;
    private JComponent firstSelectionComponent;
    private JComponent lastSelectionComponent;
    private Messenger messenger;
    private String messengerId;

    public String getNotFoundMessage() {
        return notFoundMessage;
    }

    public String getSearchAlias() {
        return searchAlias;
    }

    public Class getSearchClass() {
        return searchClass;
    }

    public SelectionFieldLayout getSelectionFieldLayout() {
        return selectionFieldLayout;
    }

    public Messenger getMessenger() {
        if (messenger == null){
            messenger = VEAUtil.getMessenger();
            messengerId = "SelectionAction_" + searchClass.getName() + "_" +
                searchAlias;
        }
        return messenger;
    }

    public void setSearchClass(Class searchClass) {
        this.searchClass = searchClass;
    }

    public void setSearchAlias(String searchAlias) {
        this.searchAlias = searchAlias;
    }

    public void setNotFoundMessage(String notFoundMessage) {
        this.notFoundMessage = notFoundMessage;
    }

    public void setSelectionFieldLayout(SelectionFieldLayout selectionFieldLayout) {
        this.selectionFieldLayout = selectionFieldLayout;
        processSelectionPanelGroup();
        createFocusTraversal();
    }

    public JLabel getVisualRepresentation() {
        return visualRepresentation;
    }

    /**
     * Ação que chama o busca de entidades
     */
    public void perform(Event evt) {

        clearMessage();
        
        if(searchView == null)
            try {
                search = Application.get()
                        .getVisualEntityManager()
                        .getSearchBuilder()
                        .create(searchClass, searchAlias);
                searchView = Application.get()
                        .getViewManager().showVisualEntity(search);
            } catch (BuilderException ex) {
                System.out.println(
                        "Falha ao construir Search em SelectionAction: " +
                        ex.getMessage());
                return ;
            }
        else
            Application.get().getViewManager().showView(searchView);

        if(searchView.getConfirmation()){
            selectionFieldLayout.setEntity(search.getSelectedEntity());
            lastSelectionComponent.requestFocus();
        }else{
            selectionFieldLayout.setEntity(null);
            showNotFoundMessage();
        }

    }

    /**
     * Ação que chama a seleção de entidaes.
     * @param e
     */
    public void actionPerformed(ActionEvent e) {

        clearMessage();

        if (entityLoader == null)
            try{
                CommonFactory factory = Application.get().getFactory();
                entityLoader = factory.createEntityLoader(
                        VEAUtil.getTopVisualEntity(this),
                        factory.createEntityList());
                entityLoader.setOwner(this);
            }catch(FactoryException fex){
                System.out.println(
                        "Falha ao construir entityLoader em SelectionAction: " +
                        fex.getMessage());
                return ;
            }

        if(selectionFieldLayout == null || !selectionFieldLayout.isModified())
            return ;

        // pega query
        Query query = selectionFieldLayout.getQuery();

        // verifica se tem algum valor em algum criterion
        boolean criterionsNull = true;
        for (Criterion c : query.getCriterions()) {
            if(c.getValue() != null){
                criterionsNull = false;
                break;
            }
        }

        // se não tem informações chama pesquisa
        if(criterionsNull){
            selectionFieldLayout.setEntity(null);
            showNotFoundMessage();
            return ;
        }

        // carrega dados
        entityLoader.load(query);

        Serializable[] entities = entityLoader.getEntityList().getEntities();

        if (entities.length == 0){
            selectionFieldLayout.setEntity(null);
            showNotFoundMessage();
            return ;
        }

        if (entities.length == 1){
            selectionFieldLayout.setEntity(entities[0]);
            return ;
        }

        if(selectionView == null)
            try{

                entitySelection = (EntitySelection) Application.get()
                        .getFactory().createVisualEntity(
                            VisualEntityType.ENTITY_SELECTION,
                            searchClass,
                            searchAlias);
                entitySelection.setOwner(this);
                entitySelection.getEntityGrid().setEntityLoader(entityLoader);

                selectionView = Application.get()
                        .getViewManager().showVisualEntity(entitySelection);

            }catch(BuilderException ex) {
                System.out.println(
                        "Falha ao construir EntitySelection em SelectionAction: " +
                        ex.getMessage());
                return ;
            }
        else
            Application.get().getViewManager().showView(selectionView);

        if(selectionView.getConfirmation()){
            selectionFieldLayout.setEntity(entitySelection.getSelectedEntity());
            lastSelectionComponent.requestFocus();
        }else{
            selectionFieldLayout.setEntity(null);
            showNotFoundMessage();
        }
    }

    /**
     * Cria a representação visual do SelectionAction.
     */
    public void createAction(){

        // cria icone
        Icon icon = BaliIcons.getIcon(BaliIcons.UPSEARCH);

        // nome
        String actionName = searchClass.getName() +
                "_selectionAction_" + searchAlias;

        JLabel label = new JLabel(icon);
        label.setName(actionName);
        label.getAccessibleContext().setAccessibleName(actionName);
        label.setFocusable(false);
        label.setToolTipText(
                BaliProperties.getString(BaliProperties.searchToolTipText));

        label.addMouseListener(new MouseListener() {
            public void mouseClicked(MouseEvent e) {
                if (e.getComponent().isEnabled())
                    perform(null);
            }
            public void mousePressed(MouseEvent e) {}
            public void mouseReleased(MouseEvent e) {}
            public void mouseEntered(MouseEvent e) {}
            public void mouseExited(MouseEvent e) {}
        });
        
        visualRepresentation = label;
    }

    /**
     * Processa os SelectionPanelFields do SelectionPanelGroup em busca
     * do primeiro e do último componente focusable.
     */
    private void processSelectionPanelGroup(){

        firstSelectionComponent = null;
        lastSelectionComponent = null;

        for (FieldComponent<JComponent> fc : selectionFieldLayout.getFieldComponents()) {
            
            if(fc instanceof FieldInput && 
                    fc.getOwner().getClass().equals(CriterionablePanelField.class)){

                if(firstSelectionComponent == null)
                    firstSelectionComponent = fc.getVisualRepresentation();

                lastSelectionComponent = fc.getVisualRepresentation();
            }
        }
    }

    /**
     * Mostra mensagem de que não há nenhum registro para a entrada da seleção.
     */
    private void showNotFoundMessage(){

        if(notFoundMessage == null)
            notFoundMessage = BaliProperties.getString(
                    BaliProperties.messageINVALID_INPUT);

        Messenger m = getMessenger();

        if(m != null){
            m.addMessage(messengerId, notFoundMessage, 3);
        }else{

            String title = null;
            if(search != null)
                title = search.getTitle();
            else if(entitySelection != null)
                title = entitySelection.getTitle();
            else
                title = BaliProperties.getString(BaliProperties.selectText);
        
            JOptionPane.showMessageDialog(firstSelectionComponent,
                    notFoundMessage,
                    title,
                    JOptionPane.ERROR_MESSAGE);
        }
        
        firstSelectionComponent.requestFocus();
    }

    public void clearMessage(){
        Messenger m = getMessenger();
        if(m != null){
            m.removeMessage(messengerId);
        }
    }

    /**
     * Cria um FocusTraversal para escutar as passagens pelo último componente
     * de seleção fo SelectionPanelGroup.<br>
     * O FocusTraversal é utiliza este objeto como ActionListener.<br>
     * O FocusTraversal já será registrado no FocusManager.<br>
     * O SelectionPanelGroup não pode ser nulo.<br>
     * Se já existir um focusTraversal ele é desregistrado.<br>
     * Este método já é chamado no setSelectionFieldLayout.
     */
    public void createFocusTraversal(){
        
        if (focusTraversal != null)
            focusTraversal.unRegister();

        if (selectionFieldLayout == null)
            return ;
        
        focusTraversal = new FocusTraversal(lastSelectionComponent, this);
        focusTraversal.register();
    }

    /**
     * Return always true.
     * @return true.
     */
    public boolean verify(Class entityClass, EventType eventType, String alias,
            String property) {
        return true;
    }

    public Class getEntityClass() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public void getSearchAlias(String searchAlias) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public boolean requestFocus() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public int getAlignment() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public Class getFieldClass() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public String getFieldName() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public String getAlias() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public EntityFieldType getEntityFieldType() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public boolean setEnabled(boolean value) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    public String setNotFoundMessage() {
        throw new UnsupportedOperationException("Not supported yet.");
    }

}
